﻿using System;
using System.IO;
using System.Runtime.CompilerServices;

namespace LINQPadLogParser
{
  public struct W3CLogLine
  {
    private readonly int _id;

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    internal W3CLogLine(int id)
    {
      _id = id;
    }

    public DateTimeWrapper DateTime
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        DateTimeWrapper value;
        if (Cache<DateTimeWrapper>.TryGet(_id, CacheKey.DateTime, out value))
          return value;
        value = new DateTimeWrapper(_id);
        Cache<DateTimeWrapper>.Set(_id, CacheKey.ClientIP, value);
        return value;
      }
    }

    public IPAddressWrapper ClientIP
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        IPAddressWrapper value;
        if (Cache<IPAddressWrapper>.TryGet(_id, CacheKey.ClientIP, out value))
          return value;
        value = new IPAddressWrapper(GetColumn(W3CColumn.ClientIP));
        Cache<IPAddressWrapper>.Set(_id, CacheKey.ClientIP, value);
        return value;
      }
    }

    public string UserName
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.UserName); }
    }

    public string SiteName
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.SiteName); }
    }

    public string ComputerName
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.ComputerName); }
    }

    public IPEndPointWrapper ServerEndPoint
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        IPEndPointWrapper value;
        if (Cache<IPEndPointWrapper>.TryGet(_id, CacheKey.ServerEndPoint, out value))
          return value;
        value = new IPEndPointWrapper(_id);
        Cache<IPEndPointWrapper>.Set(_id, CacheKey.ClientIP, value);
        return value;
      }
    }

    public string Method
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.Method); }
    }

    public UriWrapper Uri
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        UriWrapper result;
        if (Cache<UriWrapper>.TryGet(_id, CacheKey.Uri, out result))
          return result;
        result = new UriWrapper(_id);
        Cache<UriWrapper>.Set(_id, CacheKey.Uri, result);
        return result;
      }
    }

    public Int32Wrapper Status
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new Int32Wrapper(GetColumn(W3CColumn.Status)); }
    }

    public Int32Wrapper SubStatus
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return new Int32Wrapper(GetColumn(W3CColumn.SubStatus)); }
    }

    public int? Win32Status
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumnInt32(W3CColumn.Win32Status); }
    }

    public long? SentBytes
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumnInt64(W3CColumn.SentBytes); }
    }

    public long? ReceivedBytes
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumnInt64(W3CColumn.ReceivedBytes); }
    }

    public TimeSpanMillisecondsWrapper TimeTaken
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        TimeSpanMillisecondsWrapper result;
        if (Cache<TimeSpanMillisecondsWrapper>.TryGet(_id, CacheKey.TimeTaken, out result))
          return result;
        result = new TimeSpanMillisecondsWrapper(LogParserDataContext.LineManager.GetValue(_id, W3CColumn.TimeTaken));
        Cache<TimeSpanMillisecondsWrapper>.Set(_id, CacheKey.Uri, result);
        return result;
      }
    }

    public string Version
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.Version); }
    }

    public string Host
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.Host); }
    }

    public string UserAgent
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.UserAgent); }
    }

    public string Cookie
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return GetColumn(W3CColumn.Cookie); }
    }

    public SimpleUriWrapper Referer
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)]
      get
      {
        SimpleUriWrapper result;
        if (Cache<SimpleUriWrapper>.TryGet(_id, CacheKey.TimeTaken, out result))
          return result;
        result = new SimpleUriWrapper(LogParserDataContext.LineManager.GetValue(_id, W3CColumn.Referer));
        Cache<SimpleUriWrapper>.Set(_id, CacheKey.Uri, result);
        return result;
      }
    }

    internal int Id
    {
      [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _id; }
    }


    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string GetColumn(W3CColumn column)
    {
      string value;
      if (Cache<string>.TryGet(_id, (CacheKey) column, out value))
        return value;
      value = LogParserDataContext.LineManager.GetValue(Id, column);
      Cache<string>.Set(_id, (CacheKey) column, value);
      return value;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public int? GetColumnInt32(W3CColumn column)
    {
      string s = GetColumn(column);
      if (s == null)
        return null;
      return int.Parse(s);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public long? GetColumnInt64(W3CColumn column)
    {
      string s = GetColumn(column);
      if (s == null)
        return null;
      return long.Parse(s);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool HaveExtension(string s)
    {
      return Uri.AbsolutePath.EndsWith(s, StringComparison.OrdinalIgnoreCase);
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public string GetExtension()
    {
      string path = Uri.AbsolutePath;
      int length = path.Length;
      int startIndex = length;
      while (--startIndex >= 0)
      {
        char ch = path[startIndex];
        if (ch == 46)
        {
          if (startIndex != length - 1)
            return path.Substring(startIndex, length - startIndex);
          return string.Empty;
        }
        if (ch == Path.DirectorySeparatorChar || ch == Path.AltDirectorySeparatorChar || ch == Path.VolumeSeparatorChar)
          break;
      }
      return string.Empty;
    }
  }
}